package zookeeper;

import java.util.concurrent.ConcurrentHashMap;

import org.apache.thrift.TException;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zhongsou.np.zhuw.util.CommonUtil;
/**
 * 对外提供执行Thrift远程服务的接口withConnection
 * 
 * 内部维护了一个线程安全的HashMap，记录了与各个服务器的IP PORT以及与其建立的连接池
 * 内部维护Tsocket的取和回收，链接池的初始化创建等。
 * 
 * @author Administrator
 *
 */
public class ThriftConnectionManager {
    private TSocket get(String ip, int port, int timeOut) {
        return getConnectionProvider(ip, port, timeOut).getConnection();
    }

    private void release(TSocket socket, boolean hasError) {
        if (socket == null) return;
        String ip = socket.getSocket().getInetAddress().getHostAddress();
        int port = socket.getSocket().getPort();
        ConnectionProvider cp = connections.get(ip + ":" + port);
        if (cp != null) cp.returnCon(socket, hasError);
    }
    /**
     * 调用远程Thrift服务
     * action对象包含了要调用的远程服务对象以及函数名，同时记录调用后返回的结果
     * @param ip
     * @param port
     * @param timeOut
     * @param action
     */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void withConnection(String ip, int port, int timeOut, Action action) {
        long mst = System.currentTimeMillis();
        boolean hasError = false;
        TSocket socket = null;
        try {
//            socket = get(ip, port, timeOut);
        	socket = new TSocket(ip, port, timeOut);
        	socket.open();
            if (socket == null) return;
            TFramedTransport transport = new TFramedTransport(socket);
            action.client = action.getClient(factory.getProtocol(transport));
            action.val = action.doAction();
        } catch (Exception e) {
            hasError = true;
            logger.error("Exception when calling: '{}:{}'", ip, port);
            logger.error("Exception: ", CommonUtil.getStackTrace(e));
        } finally {
//            release(socket, hasError);
        	if(socket != null){
        		socket.close();
        	}
        }
        long met = System.currentTimeMillis();
        Performance.log("thirft call to '{}:{}' start at: {}, use {}ms. action={}",
                ip, port, mst, met - mst, action);
    }

    public void closeAll() {
        synchronized (this) {
            for (ConnectionProvider cp : connections.values()) cp.close();
            connections.clear();
        }
    }

    private ConnectionProvider getConnectionProvider(String ip, int port, int timeOut) {
        String key = ip + ":" + port;
        ThriftConnectionProvider newCP;
        ConnectionProvider ret = connections.get(key);
        if (ret == null) {
            newCP = new ThriftConnectionProvider();
            newCP.setServiceIP(ip);
            newCP.setServicePort(port);
            newCP.setConTimeOut(timeOut);
            ret = connections.putIfAbsent(key, newCP);
            if (ret == null)
                ret = newCP;
            else
                newCP.close();
        }
        return ret;
    }

    /**
	 * @uml.property  name="factory"
	 * @uml.associationEnd  multiplicity="(1 1)"
	 */
    private TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();
    /**
	 * @uml.property  name="connections"
	 * @uml.associationEnd  qualifier="key:java.lang.String com.zhongsou.weibo.thrift.connect.ConnectionProvider"
	 */
    private ConcurrentHashMap<String, ConnectionProvider> connections = new ConcurrentHashMap<String, ConnectionProvider>();
    private static Logger logger = LoggerFactory.getLogger(ThriftConnectionManager.class);
    
    /**
	 * Action代表一次远程函数执行，封装了获取client和调用client.doSomeThing 此类最终的使用形式只能是： action.client = action.getClient(factory.getProtocol(transport)); action.val = action.doAction(); 因此action的getClient函数要有具体的子类来实现 doAction()函数要有具体的子类来实现 泛型T约定了子类的getClient要返回的一个具体的客户端类 V来约定了子类的执行结果 因此实现Action的子类，必须清晰的知道要调用的服务属于哪个客户端类的函数，并知道调用后应返回什么类型数据
	 * @author  Administrator
	 * @param  < T  >
	 * @param  <V >
	 */
    public static abstract class Action<T extends TServiceClient, V> {
        protected T client = null;
        /**
		 * @uml.property  name="val"
		 */
        protected V val = null;

        protected abstract T getClient(TProtocol protocol);

        /**
         * call client's method to get val
         *
         * @return V value
         * @throws org.apache.thrift.TException exception
         * @throws EngineException              exception
         */
        protected abstract V doAction() throws TException, EngineException;

        /**
		 * @return
		 * @uml.property  name="val"
		 */
        public V getVal() {
            return val;
        }
    }
}
